#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <functional>

#include <unistd.h>
#include <pthread.h>

struct ThreadInfo
{
    pthread_t _tid;
    std::string _name;
};

#ifdef __TEST_DEBUG__
template<size_t MAX = 10>
#else
template<size_t MAX = 128>
#endif
class ThreadPool
{
    typedef std::function<void(void)> task_type;
    typedef ThreadPool<MAX> self;
public:
    ThreadPool()
        : _threads(MAX)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
private:
    ThreadPool(const self&) = delete;
    self& operator=(const self&) = delete;
public:
    static void* HandlerTask(void *args)
    {
        ThreadPool* tp = static_cast<ThreadPool*>(args);
        while (true)
        {
            tp->lock();
            while (tp->empty())
                tp->sleep();
            task_type t = tp->top();
            tp->pop();
            tp->unlock();
            t();
        }
    }

    void start()
    {
        for (int i = 0; i < MAX; ++i)
        {
            _threads[i]._name = "thread-" + std::to_string(i + 1);
            pthread_create(&(_threads[i]._tid), nullptr, HandlerTask, this);
            pthread_detach(_threads[i]._tid);
        }
    }

    template<class... Args>
    void emplace(Args&&... args)
    {
        lock();
        _tasks.emplace(args...);
        wake_up();
        unlock();
    }
    void push(const task_type& value)
    {
        emplace(value);
    }
    void push(task_type&& value)
    {
        emplace(std::forward<task_type>(value));
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }
public:
    void lock()
    {
        pthread_mutex_lock(&_mtx);
    }
    void unlock()
    {
        pthread_mutex_unlock(&_mtx);
    }
    void wake_up()
    {
        pthread_cond_signal(&_cond);
    }
    void sleep()
    {
        pthread_cond_wait(&_cond, &_mtx);
    }
    task_type& top()
    {
        return _tasks.front();
    }
    void pop()
    {
        _tasks.pop();
    }
    bool empty()
    {
        return _tasks.empty();
    }
    std::string get_threadname()
    {
        for (int i = 0; i < MAX; ++i)
        {
            if (_threads[i]._tid == pthread_self())
                return _threads[i]._name;
        }
        return "None";
    }
private:
    std::vector<ThreadInfo> _threads;
    std::queue<task_type> _tasks;

    pthread_mutex_t _mtx;
    pthread_cond_t _cond;
};